#include "lexer.hpp"
#include "parser.hpp"
#include "codegen.hpp"
#include "codeobj.hpp"
#include "typechecker.hpp"
#include "symtable.hpp"
#include "typed_ast.hpp"

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

void test_lexer(const char* filename) {
    auto lexer = Lexer::from_file(filename);
    Token* t = NULL;
    while ((t = lexer->next())) {
        t->print();
    }
}

void test_parser(string filename, bool verbose) {
    SymbolTable::builtin_symtable->load_builtin();
    SymbolTable symtable(SymbolTable::builtin_symtable);

    Parser parser(Lexer::from_file(filename.c_str()), &symtable);
    Node* n = parser.parse();

    cout << "After Parser : " << endl;

    if (verbose) {
        Dumper d;
        d.visit(n);
        printf("\n");
    }

    // To fill symtable.
    TypedVisitor typed_visitor(&symtable);
    Node* root = typed_visitor.visit(n);

    cout << "After Typed AST:" << endl;

    if (verbose) {
        Dumper d;
        d.visit(root);
        printf("\n");
    }

    TypeChecker type_checker(&symtable, false);
    type_checker.visit(root);

    cout << "After Typed Checker:" << endl;

    if (verbose) {
        Dumper d;
        d.visit(root);
        printf("\n");
    }

    const int len = filename.size();

    ofstream file = ofstream(filename.replace(len-3, 3, ".py"), ios::out | ios::binary);

    if (!file.good()) {
        cerr << "stream buf state is bad" << endl;
        return;
    }

    CodeGen c(&symtable, file);
    c.visit(root);

    file.close();
}

void print_usage() {
    printf("Usage: galang <command> [options] <filename>\n");
    printf("Commands:\n");
    printf("  run <filename>         Run the source file\n");
    printf("  compile <filename>     Compile the source file\n");
    printf("Options:\n");
    printf("  --version              Show version information\n");
    printf("  --verbose              Enable verbose output\n");
}

int main(int argc, char** argv) {
    bool verbose = false;
    string command;
    string filename;

    if (argc < 2) {
        print_usage();
        return -1;
    }

    // Handle --version
    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "--version") == 0) {
            printf("galang version 0.1.0\n");
            return 0;
        }
    }

    // Parse command and options
    int argi = 1;
    for (; argi < argc; ++argi) {
        if (strcmp(argv[argi], "--verbose") == 0) {
            verbose = true;
        } else if (strcmp(argv[argi], "run") == 0 || strcmp(argv[argi], "compile") == 0) {
            command = argv[argi];
            ++argi;
            filename = argv[argi];
            break;
        }
    }

    if (command.empty()) {
        print_usage();
        return -1;
    }

    if (argi >= argc) {
        printf("Missing filename.\n");
        print_usage();
        return -1;
    }

    if (verbose) {
        printf("Command: %s\n", command.c_str());
        printf("Filename: %s\n", filename.c_str());
    }

    if (command == "run") {
        // For now, just compile and run the generated Python file
        test_parser(filename, verbose);
        string pyfile = filename;
        pyfile.replace(pyfile.size() - 3, 3, ".py");
        string cmd = "python3 \"" + pyfile + "\"";
        if (verbose) {
            printf("Running: %s\n", cmd.c_str());
        }
        int ret = system(cmd.c_str());
        return ret;
    } else if (command == "compile") {
        test_parser(filename, verbose);
        return 0;
    } else {
        print_usage();
        return -1;
    }
}
